//
//  SDHttpRequestManagement.m
//  SKinDiary
//
//  Created by pcyang on 16/6/26.
//  Copyright © 2016年 tencent. All rights reserved.
//

#import "SDHttpRequestManagement.h"
#import "AFHTTPClient.h"
#import "AFNetworkActivityIndicatorManager.h"
#import "SDHTTPRequestOperation.h"
#import "JSONKit.h"

NSString* SDHttpRequest_ErrorDomain = @"WYHttpRequest_ErrorDomain";
NSString* Notification_NetWorkStatusChanged = @"Notification_NetWorkStatusChanged";
NSString* kSDHttpRequest_ResponseCode = @"SDHttpRequest_ResponseCode";
NSString* kSDHttpRequest_ResponseInfo = @"SDHttpRequest_ResponseInfo";

@implementation SDFormPartInfo

@end


@interface SDHttpRequestManagement ()
@property (nonatomic,strong) NSOperationQueue* httpOperationQueue;
@property (nonatomic,strong) NSOperationQueue* immediatelyHttpOperationQueue;
@property (nonatomic,strong) SDReachability* networkStatusReachability;

@property (nonatomic,assign) BOOL shouldReportNetworkLatency;
@end

@implementation SDHttpRequestManagement
IMP_SINGLETON

//+ (BOOL)isRequestErrorIsUserCancelled:(NSError*)error
//{
//    if( [error.domain isEqualToString:NetworkRequestErrorDomain] && error.code == ASIRequestCancelledErrorType )
//        return YES;
//    return NO;
//}

+ (SDHttpRequestManagement*)sharedHttpRequestManagement
{
    return [self sharedInstance];
}

- (NetworkStatus)currentNetworkStatus
{
    return [_networkStatusReachability currentReachabilityStatus];
}

- (id)init
{
    self = [super init];
    if( self )
    {
        [AFNetworkActivityIndicatorManager sharedManager].enabled = YES;
        
        self.httpOperationQueue = [[NSOperationQueue alloc]init];
        self.immediatelyHttpOperationQueue = [[NSOperationQueue alloc]init];
        
        self.networkStatusReachability = [SDReachability reachabilityForInternetConnection];
        [self.networkStatusReachability startNotifier];
        
        [self modifyHttpConnectMaxNum];
        [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(networkStatusChanged:) name:kSDReachabilityChangedNotification object:nil];
        
        //        [_immediatelyHttpOperationQueue addObserver:self forKeyPath:@"operationCount" options:NSKeyValueObservingOptionNew context:nil];
        
#ifdef K_TEST_CONDITION
        self.shouldReportNetworkLatency = NO;
#else
        self.shouldReportNetworkLatency =  (arc4random() % 10  == 0);
#endif
    }
    
    return self;
}

- (void)networkStatusChanged:(NSNotification *)notification
{
    [[NSNotificationCenter defaultCenter]postNotificationName:Notification_NetWorkStatusChanged object:nil];
    [self modifyHttpConnectMaxNum];
}

- (void)dealloc
{
    //    [self.immediatelyHttpOperationQueue removeObserver:self forKeyPath:@"operationCount"];
    [[NSNotificationCenter defaultCenter] removeObserver:self name:kSDReachabilityChangedNotification object:nil];
}

- (void)modifyHttpConnectMaxNum
{
    NSInteger maxHttpConnectNum = 0;
    NSInteger maxHttpConnectNumForImmediately = 0;
    //#if TARGET_IPHONE_SIMULATOR
    //	maxHttpConnectNum = 10;
    //#else
    switch ([_networkStatusReachability currentReachabilityStatus]) {
        case kReachableViaWiFi:
            maxHttpConnectNum = 6;
            maxHttpConnectNumForImmediately = 2;
            break;
        case ReachableViaWWAN:
        case kNotReachable:
        default:
            maxHttpConnectNum = 2;
            maxHttpConnectNumForImmediately = 1;
            break;
    }
    //#endif
    
    [_httpOperationQueue setMaxConcurrentOperationCount:maxHttpConnectNum];
    [_immediatelyHttpOperationQueue setMaxConcurrentOperationCount:maxHttpConnectNumForImmediately];
}

//- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
//{
//    if( [object isKindOfClass:[NSOperationQueue class]] && [keyPath isEqualToString:@"operationCount"] )
//        [_httpOperationQueue setSuspended:[[change objectForKey:NSKeyValueChangeNewKey]unsignedIntegerValue]>0];
//}

- (void)addHttpRequestOperation:(AFHTTPRequestOperation*)operation
{
    [_httpOperationQueue addOperation:operation];
}

- (void)addOperation:(NSOperation*)operation
{
    [_httpOperationQueue addOperation:operation];
}

- (void)addHttpRequest:(NSOperation*)operation withPriority:(WYHttpRequestPriority)priority
{
    switch (priority) {
        case WYHttpRequestPriority_VeryLow:
            [operation setQueuePriority:NSOperationQueuePriorityVeryLow];
            break;
        case WYHttpRequestPriority_Low:
            [operation setQueuePriority:NSOperationQueuePriorityLow];
            break;
        case WYHttpRequestPriority_High:
            [operation setQueuePriority:NSOperationQueuePriorityHigh];
            break;
        case WYHttpRequestPriority_Immediately:
        case WYHttpRequestPriority_VeryHigh:
            [operation setQueuePriority:NSOperationQueuePriorityVeryHigh];
            break;
        case WYHttpRequestPriority_Normal:
        default:
            [operation setQueuePriority:NSOperationQueuePriorityNormal];
            break;
    }
    
    if( priority == WYHttpRequestPriority_Immediately && [_networkStatusReachability currentReachabilityStatus] == kReachableViaWiFi )
        [_immediatelyHttpOperationQueue addOperation:operation];
    else
        [_httpOperationQueue addOperation:operation];
}

- (NetworkStatus)getNetworkStatus
{
    return [_networkStatusReachability currentReachabilityStatus];
}

//+ (void)startHttpRequset:(ASIHTTPRequest*)request
//                priority:(WYHttpRequestPriority)priority
//{
//    [[SDHttpRequestManagement sharedInstance]addHttpRequest:request withPriority:priority];
//}


+ (SDHTTPRequestOperation*)startHttpRequestWithURL:(NSString*)url
                                         jsonParser:(BOOL)jsonParser
                                     requestCookies:(NSDictionary*)requestCookies
                                           priority:(WYHttpRequestPriority)priority
                                  objectParserBlock:(id (^)(id data,NSError** error))block
                                    completionQueue:(dispatch_queue_t)completionQueue
                                         completion:(void (^)(id data, NSError *error))completion

{
    int errorCode = WYHttpRequest_Errorcode_None;
    if( !url )errorCode = WYHttpRequest_Errorcode_InvalidParameter;
    if ( [[SDHttpRequestManagement sharedInstance] getNetworkStatus] == kNotReachable )
        errorCode = WYHttpRequest_Errorcode_NoActiveNetwork;
    
    if( errorCode != WYHttpRequest_Errorcode_None )
    {
        dispatch_async(dispatch_get_main_queue(), ^{
            if(completion)completion(nil,[NSError errorWithDomain:WYHttpRequest_ErrorDomain code:errorCode userInfo:nil]);
        });
        
        return nil;
    }
    
    NSMutableURLRequest* request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:url]
                                                           cachePolicy:NSURLRequestUseProtocolCachePolicy
                                                       timeoutInterval:20.0];
    
    SDHTTPRequestOperation* operation = [[SDHTTPRequestOperation alloc]initWithRequest:request];
    operation.shouldReportNetworkLatency = [SDHttpRequestManagement sharedInstance].shouldReportNetworkLatency;
    operation.jsonParsed = jsonParser;
    operation.requestCookies = requestCookies;
    operation.objectParserBlock = block;
    if( completion )
    {
        __weak SDHTTPRequestOperation* weakOpeartion = operation;
        [operation setCompletionBlock:^{
            __strong SDHTTPRequestOperation* strongOpeartion = weakOpeartion;
            NSData* data = strongOpeartion.parsedObject;
            NSError* error = strongOpeartion.error;
            dispatch_async(completionQueue ?: dispatch_get_main_queue(), ^{
                completion(data,error);
            });
        }];
    }
    
    [[SDHttpRequestManagement sharedInstance]addHttpRequest:operation withPriority:priority];
    
    return operation;
}



+ (SDHTTPRequestOperation*)startHttpRequestWithURL:(NSString*)url
                                         jsonParser:(BOOL)jsonParser
                                     requestCookies:(NSDictionary*)requestCookies
                                           priority:(WYHttpRequestPriority)priority
                                  objectParserBlock:(id (^)(id data,NSError** error))block
                                         completion:(void (^)(id data, NSError *error))completion
{
    return [self startHttpRequestWithURL:url
                              jsonParser:jsonParser
                          requestCookies:requestCookies
                                priority:priority
                       objectParserBlock:block
                         completionQueue:nil
                              completion:completion];
}

+ (SDHTTPRequestOperation*)startHttpRequestAndParsingResultWithURL:(NSString*)url
                                                           priority:(WYHttpRequestPriority)priority
                                                     requestCookies:(NSDictionary*)requestCookies
                                                  objectParserBlock:(id (^)(id data, NSError** error))block
                                                         completion:(void (^)(id data, NSError *error))completion
{
    return [self startHttpRequestWithURL:url
                              jsonParser:YES
                          requestCookies:requestCookies
                                priority:priority
                       objectParserBlock:^id(id data, NSError **error)
            {
                if( block )
                    return block(data,error);
                else
                    return data;
            }
                              completion:^(id data, NSError *error)
            {
                if(completion)
                    completion(data,error);
            }];
}



+ (SDHTTPRequestOperation*)startHttpPostRequestWithURL:(NSString*)url
                                              postValue:(NSDictionary*)postValue
                                         requestCookies:(NSDictionary*)requestCookies
                                             jsonParser:(BOOL)jsonParser
                                               priority:(WYHttpRequestPriority)priority
                                      objectParserBlock:(id (^)(id data,NSError** error))block
                                             completion:(void (^)(id data, NSError *error))completion
{
    return [self startHttpPostRequestWithURL:url
                                   postValue:postValue
                              requestCookies:requestCookies
                                  jsonParser:jsonParser
                                    priority:priority
                           objectParserBlock:block
                             completionQueue:nil
                                  completion:completion];
}

+(NSString*)daataTOjsonString:(id)object
{
    NSString *jsonString = nil;
    NSError *error;
    NSData *jsonData = [NSJSONSerialization dataWithJSONObject:object
                                                       options:NSJSONWritingPrettyPrinted // Pass 0 if you don't care about the readability of the generated string
                                                         error:&error];
    if (! jsonData) {
        NSLog(@"Got an error: %@", error);
    } else {
        jsonString = [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding];
    }
    return jsonString;
}

+ (SDHTTPRequestOperation*)startHttpPostRequestWithURL:(NSString*)url
                                              postValue:(NSDictionary*)postValue
                                         requestCookies:(NSDictionary*)requestCookies
                                             jsonParser:(BOOL)jsonParser
                                               priority:(WYHttpRequestPriority)priority
                                      objectParserBlock:(id (^)(id data,NSError** error))block
                                        completionQueue:(dispatch_queue_t)completionQueue
                                             completion:(void (^)(id data, NSError *error))completion
{
    int errorCode = WYHttpRequest_Errorcode_None;
    if( !url )errorCode = WYHttpRequest_Errorcode_InvalidParameter;
    if ( [[SDHttpRequestManagement sharedInstance] getNetworkStatus] == kNotReachable )
        errorCode = WYHttpRequest_Errorcode_NoActiveNetwork;
    
    if( errorCode != WYHttpRequest_Errorcode_None )
    {
        dispatch_async(dispatch_get_main_queue(), ^{
            if(completion)completion(nil,[NSError errorWithDomain:WYHttpRequest_ErrorDomain code:errorCode userInfo:nil]);
        });
        
        return nil;
    }
    
    NSMutableURLRequest* request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:url]
                                                           cachePolicy:NSURLRequestUseProtocolCachePolicy
                                                       timeoutInterval:20.0];
    [request setHTTPMethod:@"POST"];
    request = [request constructMultipartFormDataWithBlock:^(id<AFMultipartFormData> formData) {
        NSString *str = [SDHttpRequestManagement daataTOjsonString:postValue];
        [formData appendPartWithFormData:[str dataUsingEncoding:NSUTF8StringEncoding] name:@""];
//        [postValue enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop) {
//            if( [obj isKindOfClass:[NSData class]] )
//            {
//                [formData appendPartWithFormData:obj name:key];
//            }
//            else if( [obj isKindOfClass:[NSString class]] )
//            {
//                [formData appendPartWithFormData:[obj dataUsingEncoding:NSUTF8StringEncoding] name:key];
//            }
//            else if([obj isKindOfClass:[NSArray class]])
//            {
//                NSArray* tmpArr = (NSArray*)obj;
//                
//                for(SDFormPartInfo* partInfo in tmpArr)
//                {
//                    if([partInfo isKindOfClass:[SDFormPartInfo class]])
//                    {
//                        if(partInfo.fileUrl && partInfo.partName && partInfo.fileName && partInfo.mimeType)
//                            [formData appendPartWithFileURL:partInfo.fileUrl name:partInfo.partName fileName:partInfo.fileName mimeType:partInfo.mimeType error:nil];
//                    }
//                }
//            }
//        }];
    }];
    
    SDHTTPRequestOperation* operation = [[SDHTTPRequestOperation alloc]initWithRequest:request];
    operation.shouldReportNetworkLatency = [SDHttpRequestManagement sharedInstance].shouldReportNetworkLatency;
    operation.jsonParsed = jsonParser;
    operation.requestCookies = requestCookies;
    operation.objectParserBlock = block;
    if( completion )
    {
        __weak SDHTTPRequestOperation* weakOpeartion = operation;
        [operation setCompletionBlock:^{
            __strong SDHTTPRequestOperation* strongOpeartion = weakOpeartion;
            NSData* data = strongOpeartion.parsedObject;
            NSError* error = strongOpeartion.error;
            dispatch_async(completionQueue ?: dispatch_get_main_queue(), ^{
                completion(data,error);
            });
        }];
    }
    
    [[SDHttpRequestManagement sharedInstance]addHttpRequest:operation withPriority:priority];
    
    return operation;
}

+ (SDHTTPRequestOperation*)startHttpPutRequestWithURL:(NSString*)url
                                             postValue:(NSDictionary*)postValue
                                        requestCookies:(NSDictionary*)requestCookies
                                            jsonParser:(BOOL)jsonParser
                                              priority:(WYHttpRequestPriority)priority
                                     objectParserBlock:(id (^)(id data,NSError** error))block
                                       completionQueue:(dispatch_queue_t)completionQueue
                                            completion:(void (^)(id data, NSError *error))completion
{
    int errorCode = WYHttpRequest_Errorcode_None;
    if( !url )errorCode = WYHttpRequest_Errorcode_InvalidParameter;
    if ( [[SDHttpRequestManagement sharedInstance] getNetworkStatus] == kNotReachable )
        errorCode = WYHttpRequest_Errorcode_NoActiveNetwork;
    
    if( errorCode != WYHttpRequest_Errorcode_None )
    {
        dispatch_async(dispatch_get_main_queue(), ^{
            if(completion)completion(nil,[NSError errorWithDomain:WYHttpRequest_ErrorDomain code:errorCode userInfo:nil]);
        });
        
        return nil;
    }
    
    NSMutableURLRequest* request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:url]
                                                           cachePolicy:NSURLRequestUseProtocolCachePolicy
                                                       timeoutInterval:20.0];
    [request setHTTPMethod:@"PUT"];
    request = [request constructMultipartFormDataWithBlock:^(id<AFMultipartFormData> formData) {
        NSString *str = [SDHttpRequestManagement daataTOjsonString:postValue];
        [formData appendPartWithFormData:[str dataUsingEncoding:NSUTF8StringEncoding] name:@""];
        //        [postValue enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop) {
        //            if( [obj isKindOfClass:[NSData class]] )
        //            {
        //                [formData appendPartWithFormData:obj name:key];
        //            }
        //            else if( [obj isKindOfClass:[NSString class]] )
        //            {
        //                [formData appendPartWithFormData:[obj dataUsingEncoding:NSUTF8StringEncoding] name:key];
        //            }
        //            else if([obj isKindOfClass:[NSArray class]])
        //            {
        //                NSArray* tmpArr = (NSArray*)obj;
        //
        //                for(SDFormPartInfo* partInfo in tmpArr)
        //                {
        //                    if([partInfo isKindOfClass:[SDFormPartInfo class]])
        //                    {
        //                        if(partInfo.fileUrl && partInfo.partName && partInfo.fileName && partInfo.mimeType)
        //                            [formData appendPartWithFileURL:partInfo.fileUrl name:partInfo.partName fileName:partInfo.fileName mimeType:partInfo.mimeType error:nil];
        //                    }
        //                }
        //            }
        //        }];
    }];
    
    SDHTTPRequestOperation* operation = [[SDHTTPRequestOperation alloc]initWithRequest:request];
    operation.shouldReportNetworkLatency = [SDHttpRequestManagement sharedInstance].shouldReportNetworkLatency;
    operation.jsonParsed = jsonParser;
    operation.requestCookies = requestCookies;
    operation.objectParserBlock = block;
    if( completion )
    {
        __weak SDHTTPRequestOperation* weakOpeartion = operation;
        [operation setCompletionBlock:^{
            __strong SDHTTPRequestOperation* strongOpeartion = weakOpeartion;
            NSData* data = strongOpeartion.parsedObject;
            NSError* error = strongOpeartion.error;
            dispatch_async(completionQueue ?: dispatch_get_main_queue(), ^{
                completion(data,error);
            });
        }];
    }
    
    [[SDHttpRequestManagement sharedInstance]addHttpRequest:operation withPriority:priority];
    
    return operation;
}

+ (SDHTTPRequestOperation*)startRequestWithHttpMethod:(NSString*)httpMethod
                                        url:(NSString*)url
                                            postValue:(NSDictionary*)postValue
                                       requestCookies:(NSDictionary*)requestCookies
                                           jsonParser:(BOOL)jsonParser
                                             priority:(WYHttpRequestPriority)priority
                                    objectParserBlock:(id (^)(id data,NSError** error))block
                                      completionQueue:(dispatch_queue_t)completionQueue
                                           completion:(void (^)(id data, NSError *error))completion
{
    int errorCode = WYHttpRequest_Errorcode_None;
    if( !url )errorCode = WYHttpRequest_Errorcode_InvalidParameter;
    if ( [[SDHttpRequestManagement sharedInstance] getNetworkStatus] == kNotReachable )
        errorCode = WYHttpRequest_Errorcode_NoActiveNetwork;
    
    if( errorCode != WYHttpRequest_Errorcode_None )
    {
        dispatch_async(dispatch_get_main_queue(), ^{
            if(completion)completion(nil,[NSError errorWithDomain:WYHttpRequest_ErrorDomain code:errorCode userInfo:nil]);
        });
        
        return nil;
    }
    
    NSMutableURLRequest* request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:url]
                                                           cachePolicy:NSURLRequestUseProtocolCachePolicy
                                                       timeoutInterval:20.0];
    [request setHTTPMethod:httpMethod];
    request = [request constructMultipartFormDataWithBlock:^(id<AFMultipartFormData> formData) {
        NSString *str = [SDHttpRequestManagement daataTOjsonString:postValue];
        [formData appendPartWithFormData:[str dataUsingEncoding:NSUTF8StringEncoding] name:@""];
    
    }];
    
    SDHTTPRequestOperation* operation = [[SDHTTPRequestOperation alloc]initWithRequest:request];
    operation.shouldReportNetworkLatency = [SDHttpRequestManagement sharedInstance].shouldReportNetworkLatency;
    operation.jsonParsed = jsonParser;
    operation.requestCookies = requestCookies;
    operation.objectParserBlock = block;
    if( completion )
    {
        __weak SDHTTPRequestOperation* weakOpeartion = operation;
        [operation setCompletionBlock:^{
            __strong SDHTTPRequestOperation* strongOpeartion = weakOpeartion;
            NSData* data = strongOpeartion.parsedObject;
            NSError* error = strongOpeartion.error;
            dispatch_async(completionQueue ?: dispatch_get_main_queue(), ^{
                completion(data,error);
            });
        }];
    }
    
    [[SDHttpRequestManagement sharedInstance]addHttpRequest:operation withPriority:priority];
    
    return operation;
}
@end
